package com.middlegroup.subjectpay.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.middlegroup.common.enums.ConfigKeyEnums;
import com.middlegroup.common.exception.ErrorMsg;
import com.middlegroup.common.model.NotifyRequest;
import com.middlegroup.common.model.SubjectpayQueryRequest;
import com.middlegroup.common.model.TransResult;
import com.middlegroup.common.utils.DateUtils;
import com.middlegroup.subjectpay.dto.BindCardDto;
import com.middlegroup.subjectpay.dto.TransRequest;
import com.middlegroup.subjectpay.entity.*;
import com.middlegroup.subjectpay.service.*;
import com.middlegroup.subjectpay.service.kjtpay.*;
import com.middlegroup.subjectpay.service.ucfpay.AESCoder;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import sun.security.rsa.RSAPrivateCrtKeyImpl;

import javax.annotation.Resource;
import javax.crypto.Cipher;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

/**
 * Created by pc on 2019/1/14.
 */
@Service
@Slf4j
public class KjtpayServiceImpl implements KjtpayService {
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private KjtpayBankCodeService kjtpayBankCodeService;
    @Resource
    private SubjectChannelConfigService subjectChannelConfigService;
    @Resource
    private SubjectOutThirdBillsService subjectOutThirdBillsService;
    @Resource
    private SubjectOutNotifyLogService subjectOutNotifyLogService;
    @Resource
    private SubjectOutBillsService subjectOutBillsService;
    @Resource
    private SubjectPayNotifyUtil subjectPayNotifyUtil;
    @Resource
    private ThirdProtocolBindLogService thirdProtocolBindLogService;
    @Resource
    private SubjectInThirdBillsService subjectInThirdBillsService;
    @Resource
    private SubjectInNotifyLogService subjectInNotifyLogService;
    @Resource
    private SubjectInBillsService subjectInBillsService;

    @Override
    public TransResult df(TransRequest request) {
        TransResult transResult = new TransResult();
        transResult.setBillNo(request.getBillNo());
        transResult.setMerchantBillNo(request.getMerchantBillNo());
        transResult.setAmount(request.getAmount());
        transResult.setTransStatus(0);
        transResult.setTransTime(System.currentTimeMillis());

        KjtpayBankCode kjtpayBankCode = kjtpayBankCodeService.getBankCode(request.getCardBin());
        if (null == kjtpayBankCode || StringUtils.isBlank(kjtpayBankCode.getBankCode())){
            transResult.setTransStatus(2);
            transResult.setMsg(ErrorMsg.NONSUPPORT_BANK);
            return transResult;
        }

        String category=request.getChannelCode().getCode();

        String merchantNo=subjectChannelConfigService.getConfigString(category, ConfigKeyEnums.MERCHANTNO.getName());
        String requestUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.REQUESTURL.getName());
        String privateKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PRIVATEKEY.getName());
        String publicKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PUBLICKEY.getName());
        String callBackUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.CALLBACKURL.getName());
        String MD5Key=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MD5KEY.getName());

//        String merchantNo="200002971292";
//        String requestUrl="https://gateway.kjtpay.com/recv.do";
//        String privateKey="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBANtzmNNrJAtZPrM/XC4NLKgzHypAbyiizC95kzxBhFkZP1LsxJdfMVN8EBKMrRymtJigLnbXNimn+LbPsXcA1/fN4MNwe1UPYFeWz3/9LaQo3whH7gNWNHmewZL6a1R3YVHF5UI3RSSSJLEEDAqbJ9TEtQyCjhnOrDCSygbRlc5hAgMBAAECgYEAj7tKoU5PMU+mjWg2S8M9OH0jMF07cPYPnhiWoqSWtnSe0QVaTXp/qBpdvIh+6YEWT9C6HgQe6XGmqLWnmjXnPKPl2RytAz22yJfx9DW/K5AwJHqLagljaNyxYEHNKZOyiq/98QkiMeBdnMChSZCusZG+ZGuoaam+5HLeXRnS47ECQQD1LbMG/dOwL8lGve+daauCzXfg+h5eaUrkdbk+HjQHA55wTdHWssoNRunHCw9qWkvf23JFhcVMrqljdNLAq4lPAkEA5SM0qIeAZr1vtoPz/7+S7heMSGrioBDspUogkbvGbLcfdYHd7hc88i2YG+1VpCtlJ6W59G7zhRCBI4f4KYjhTwJAUHhtWBAbWKp2f3d7m8jA+C+D9x1gtlAMuqtA33h+TwcLC4UfKD6KPo1V0XR53MhCOvkFqnfbZ+2zvubq/T3P1QJALYA+NrEhqEU5585wJ3sNXEZEzySaeBqkQ3Zi3U2ia3g5wQXW4i6vJZA0QmdwuDb90U+xi3500Rmj60BW4TiTNQJAEl6P9vTJ4eDj72lsevEbuBD5UaSRit8JoyQXWtD1+dJ7/26BLDM6c6tb07kO51myCUKG6UT13epojbqPzBGP0Q==";
//        String publicKey="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCf6rBYHQJRXR8P1KYZPBKbuzygPLOv7TLQwwR7LFYEzS1DYEQZ1kt+2LLEcOdV6M762RBvPfdId2RrdSnl4TUcXe+qDkpsblugjNWn2CJkrCfwr0JYrnZlRNf6HZkjG3u07iXygWs1+eFuCVEz5/zQae1dW95qKrsGoo8AoJx9DQIDAQAB";
//        String callBackUrl="https://api.yihuabao.cn/yhb/api/1.0.0/notify/kjtpayDfNotify";
//        String MD5Key="该渠道不用这个字段值";

        if(StringUtils.isBlank(requestUrl) || StringUtils.isBlank(merchantNo) || StringUtils.isBlank(privateKey) || StringUtils.isBlank(publicKey) || StringUtils.isBlank(callBackUrl) || StringUtils.isBlank(MD5Key)){
            transResult.setTransStatus(2);
            transResult.setMsg(ErrorMsg.INCOMPLETE_CONFIG_INFO);
            return transResult;
        }

        Map<String,Object> params = new HashMap<String, Object>();
        //平台(商户)订单号
        params.put("out_trade_no",request.getBillNo());
        //出款快捷通会员标识类型，默认1  1-快捷通会员ID  2-快捷通会员登录号
        params.put("payer_identity_type","1");
        //出款账号
        params.put("payer_identity",merchantNo);
        //金额
        params.put("amount",request.getAmount().setScale(2).toString());
        //币种，默认人民币
        params.put("currency","CNY");
        params.put("bank_card_no",request.getCardNo());
        //银行卡账户名，不能包含数字
        params.put("bank_account_name",request.getName());
        params.put("bank_code",kjtpayBankCode.getBankCode());
        params.put("bank_name",kjtpayBankCode.getBankName());
        //业务产品码10220-付款到卡（次日） 10221-付款到卡（普通）
        params.put("biz_product_code","10221");
        //支付产品码14-付款到银行卡-对私 15-付款到银行卡-对公
        params.put("pay_product_code","14");
        params.put("notify_url",callBackUrl);
        MultiValueMap<String, String> postParameters = this.generateRequestParams(params,KjtConfig.df_service,merchantNo,privateKey);

        SubjectOutThirdBills subjectOutThirdBills=new SubjectOutThirdBills();
        subjectOutThirdBills.setSubjectCode(request.getSubjectCode());
        subjectOutThirdBills.setMerchantBillNo(request.getMerchantBillNo());
        subjectOutThirdBills.setBillNo(request.getBillNo());
        subjectOutThirdBills.setStatus(0);
        subjectOutThirdBills.setAmount(request.getAmount());
        subjectOutThirdBills.setRequestUrl(requestUrl);
        subjectOutThirdBills.setRequestText(JSON.toJSONString(params));
        subjectOutThirdBills.setBeginTime(System.currentTimeMillis());
        subjectOutThirdBills.setServiceType(request.getServiceType());

        String str = null;
        try {
            str = restTemplate.postForObject(requestUrl,postParameters,String.class);
        } catch (Exception e) {
            log.error("调用快捷通代付接口异常,billNo:{},error:{}",request.getBillNo(),e);
            transResult.setMsg(ErrorMsg.PAY_INTERFACE_CALL_FAIL);
        }

        subjectOutThirdBills.setResponseText((StringUtils.isNotBlank(str) && str.length()>4000)?str.substring(0,4000):str);
        subjectOutThirdBills.setEndTime(System.currentTimeMillis());

        if (StringUtils.isBlank(str)) {
            //默认交易处理中
            log.error("调用快捷通代付接口返回空,billNo:{}",request.getBillNo());
            transResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
        }else {
            //TODO biz_content中字段的顺序不能变
            Gson gson = new Gson();
            Map<String, String> map = new HashMap<String, String>();
            map = gson.fromJson(str, map.getClass());
            if (null != map) {
                String sign = map.get("sign");
                String charset = map.get("charset");
                String bizContent = map.get("biz_content") == null ? null : JSON.toJSONString(map.get("biz_content"));
                map.remove("biz_content");
                map.put("biz_content", bizContent);
                boolean flag = false;
                try {
                    flag = this.verify(map, sign, charset, publicKey);
                } catch (Exception e) {
                    transResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                    log.error("代付解析快捷通返回数据出错,str:{},erroe:{}", str, e);
                }
                if (flag) {
                    //验签成功
                    //网关返回码
                    String code = map.get("code");
                    String msg = map.get("msg");
                    String biz_content = map.get("biz_content");
                    transResult.setMsg(msg);

                    if (StringUtils.equals("S10000", code)) {
                        //接口调用成功
                        JSONObject jsonObject = JSONObject.parseObject(biz_content);
                        //快捷通订单号
                        String trade_no = jsonObject.getString("trade_no");
                        //订单状态，S-成功 P-处理中 F-失败
                        String status = jsonObject.getString("status");
                        transResult.setTransNo(trade_no);
                        if (StringUtils.equals("S", status)) {
                            transResult.setTransTime(System.currentTimeMillis());
                            transResult.setTransStatus(1);
                            subjectOutThirdBills.setStatus(1);
                        } else if (StringUtils.equals("F", status)) {
                            transResult.setTransStatus(2);
                            subjectOutThirdBills.setStatus(2);
                        } else {//处理中
                            transResult.setTransStatus(0);
                            subjectOutThirdBills.setStatus(0);
                        }
                    }
                } else {
                    transResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                }
            } else {
                transResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
            }
        }
        try {
            subjectOutThirdBillsService.insert(subjectOutThirdBills);
        } catch (Exception e) {
            log.error("快捷通代付插入第三方订单表异常,billNo:{},error:{}",request.getBillNo(),e);
        }

        return transResult;
    }

    @Override
    public TransResult dfQuery(SubjectpayQueryRequest request) {
        TransResult transResult = new TransResult();
        transResult.setBillNo(request.getBillNo());
        transResult.setMerchantBillNo(request.getMerchantBillNo());
        transResult.setAmount(request.getAmount());
        transResult.setTransStatus(0);
        transResult.setTransTime(System.currentTimeMillis());

        String category=request.getChannelCode();

        String merchantNo=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MERCHANTNO.getName());
        String requestUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.REQUESTURL.getName());
        String privateKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PRIVATEKEY.getName());
        String publicKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PUBLICKEY.getName());
        String callBackUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.CALLBACKURL.getName());
        String MD5Key=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MD5KEY.getName());

        if(StringUtils.isBlank(requestUrl) || StringUtils.isBlank(merchantNo) || StringUtils.isBlank(privateKey) || StringUtils.isBlank(publicKey) || StringUtils.isBlank(callBackUrl) || StringUtils.isBlank(MD5Key)){
            transResult.setTransStatus(2);
            transResult.setMsg(ErrorMsg.INCOMPLETE_CONFIG_INFO);
            return transResult;
        }

        Map<String,Object> params = new HashMap<String, Object>();
        //平台(商户)订单号
        params.put("out_trade_no",request.getBillNo());
        MultiValueMap<String, String> postParameters = this.generateRequestParams(params,KjtConfig.df_query,merchantNo,privateKey);

        SubjectOutThirdBills subjectOutThirdBills=new SubjectOutThirdBills();
        subjectOutThirdBills.setSubjectCode(request.getSubjectCode());
        subjectOutThirdBills.setMerchantBillNo(request.getMerchantBillNo());
        subjectOutThirdBills.setBillNo(request.getBillNo());
        subjectOutThirdBills.setStatus(0);
        subjectOutThirdBills.setAmount(request.getAmount());
        subjectOutThirdBills.setRequestUrl(requestUrl);
        subjectOutThirdBills.setRequestText(JSON.toJSONString(params));
        subjectOutThirdBills.setBeginTime(System.currentTimeMillis());
        subjectOutThirdBills.setServiceType(request.getServiceType());

        String str = null;
        try {
            str = restTemplate.postForObject(requestUrl,postParameters,String.class);
        } catch (Exception e) {
            log.error("调用快捷通代付查询接口异常,billNo:{},error:{}",request.getBillNo(),e);
            transResult.setMsg(ErrorMsg.PAY_INTERFACE_CALL_FAIL);
        }

        subjectOutThirdBills.setResponseText((StringUtils.isNotBlank(str) && str.length()>4000)?str.substring(0,4000):str);
        subjectOutThirdBills.setEndTime(System.currentTimeMillis());

        if (StringUtils.isBlank(str)) {
            //默认交易处理中
            log.error("调用快捷通代付查询接口返回空,billNo:{}",request.getBillNo());
            transResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
        }else {
            //TODO biz_content中字段的顺序不能变
            Gson gson = new Gson();
            Map<String, String> map = new HashMap<String, String>();
            map = gson.fromJson(str, map.getClass());
            if (null != map) {
                String sign = map.get("sign");
                String charset = map.get("charset");
                String bizContent = map.get("biz_content") == null ? null : JSON.toJSONString(map.get("biz_content"));
                map.remove("biz_content");
                map.put("biz_content", bizContent);
                boolean flag = false;
                try {
                    flag = this.verify(map, sign, charset, publicKey);
                } catch (Exception e) {
                    transResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                    log.error("代付解析快捷通返回数据出错,str:{},erroe:{}", str, e);
                }
                if (flag) {
                    //验签成功
                    //网关返回码
                    String code = map.get("code");
                    String biz_content = map.get("biz_content");
                    String msg = map.get("sub_msg");
                    transResult.setMsg(msg);

                    if (StringUtils.equals("S10000", code)) {
                        //接口调用成功
                        JSONObject jsonObject = JSONObject.parseObject(biz_content);
                        //快捷通订单号
                        String trade_no = jsonObject.getString("trade_no");
                        //交易状态对应的时间，格式yyyyMMddHHmmss
                        String transTime=jsonObject.getString("result_time");
                        //转账状态 submitted提交银行成功 failed出款失败 success出款成功 如为failed 时错误编码和错误原因放入sub_code和sub_msg中
                        String status = jsonObject.getString("status");
                        transResult.setTransNo(trade_no);
                        if (StringUtils.isNotBlank(transTime) && !StringUtils.equals("NULL",transTime) && !StringUtils.equals("null",transTime)){
                            transResult.setTransTime(DateUtils.convert(transTime,DateUtils.DATE_TIME_FORMAT).getTime());
                        }
                        if (StringUtils.equals("success", status)) {
                            transResult.setTransStatus(1);
                            subjectOutThirdBills.setStatus(1);
                        } else if (StringUtils.equals("failed", status)) {
                            transResult.setTransStatus(2);
                            subjectOutThirdBills.setStatus(2);
                        } else {//处理中
                            transResult.setTransStatus(0);
                            subjectOutThirdBills.setStatus(0);
                        }
                    }
                } else {
                    transResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                }
            } else {
                transResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
            }
        }
        try {
            subjectOutThirdBillsService.insert(subjectOutThirdBills);
        } catch (Exception e) {
            log.error("快捷通代付插入第三方订单表异常,billNo:{},error:{}",request.getBillNo(),e);
        }

        return transResult;
    }

    @Override
    public String dfNotify(KjtpayDfCallBackRequest request) {
        String result ="";

        //记录回调原始数据
        SubjectOutNotifyLog subjectOutNotifyLog = new SubjectOutNotifyLog();
        subjectOutNotifyLog.setBillNo(request.getOuter_trade_no());
        subjectOutNotifyLog.setCallbackText(JSONObject.toJSONString(request));
        //默认回调处理状态为处理中
        subjectOutNotifyLog.setStatus(0);
        //默认订单交易状态为处理中
        subjectOutNotifyLog.setTransStatus(0);
        subjectOutNotifyLog.setCallbackText(JSONObject.toJSONString(request));
        subjectOutNotifyLog.setMsg("回调处理中");
        boolean rt = subjectOutNotifyLogService.insert(subjectOutNotifyLog);
        if (!rt){
            log.error("插入回调日志表失败,request:{}",request);
            return result;
        }

        //查询回调对应的订单信息
        SubjectOutBills subjectOutBillsFind = new SubjectOutBills();
        subjectOutBillsFind.setBillNo(request.getOuter_trade_no());
        SubjectOutBills subjectOutBills = subjectOutBillsService.getLastOneOutBills(subjectOutBillsFind);
        if(null==subjectOutBills){
            log.error("未查询到该笔订单,request:{}",request);
            return result;
        }
        subjectOutNotifyLog.setSubjectCode(subjectOutBills.getSubjectCode());
        subjectOutNotifyLog.setMerchantBillNo(subjectOutBills.getMerchantBillNo());

        //转发到支付中台的回调对象
        NotifyRequest notifyRequest=new NotifyRequest();
        notifyRequest.setMerchantBillNo(subjectOutBills.getMerchantBillNo());
        notifyRequest.setBillNo(subjectOutBills.getBillNo());
        notifyRequest.setTransStatus(0);
        notifyRequest.setMsg("回调处理中");
        notifyRequest.setTransTime(System.currentTimeMillis());
        notifyRequest.setAmount(subjectOutBills.getAmount());

        String category=subjectOutBills.getChannelCode();
        String publicKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PUBLICKEY.getName());

        Map<String,String> map = JSONObject.parseObject(JSONObject.toJSONString(request),Map.class);
        if (null != map) {
            String sign = map.get("sign");
            map.put("_input_charset", "UTF-8");
            boolean flag = false;
            try {
                flag =this.verify(map, sign, "UTF-8", publicKey);
            } catch (Exception e) {
                log.error("代付解析快捷通返回数据出错,notify_id:{},error:{}",request.getNotify_id(),e);
            }

            if(flag){
                String transNo=request.getInner_trade_no();
                //WITHDRAWAL_SUCCESS:成功  WITHDRAWAL_FAIL：失败 RETURN_TICKET：退票
                String status=request.getWithdrawal_status();
                //支付时间 要转换 yyyyMMddHHmmss
                String tradeTime=request.getGmt_withdrawal();
                //失败原因
                String fail_reason=request.getFail_reason();

                subjectOutNotifyLog.setStatus(1);
                subjectOutNotifyLog.setTransNo(transNo);
                subjectOutNotifyLog.setMsg(fail_reason);
                notifyRequest.setTransNo(transNo);
                notifyRequest.setMsg(fail_reason);

                if ((subjectOutBills.getStatus().intValue() == 0 || subjectOutBills.getStatus().intValue() == 3)){
                    if(StringUtils.isNotBlank(status)){
                        if(StringUtils.equals("WITHDRAWAL_SUCCESS",status)){
                            //支付成功
                            if(StringUtils.isNotBlank(tradeTime)){
                                Long time=DateUtils.convert(tradeTime,DateUtils.DATE_TIMESTAMP_SHORT_FORMAT).getTime();
                                notifyRequest.setTransTime(time);
                            }else{
                                notifyRequest.setTransTime(System.currentTimeMillis());
                            }

                            notifyRequest.setTransStatus(1);
                            subjectOutNotifyLog.setTransStatus(1);
                        }else if(StringUtils.equals("WITHDRAWAL_FAIL",status) || StringUtils.equals("RETURN_TICKET",status)){
                            //支付失败
                            notifyRequest.setTransStatus(2);
                            subjectOutNotifyLog.setTransStatus(2);
                        } else {
                            //支付处理中
                            notifyRequest.setTransStatus(0);
                            subjectOutNotifyLog.setTransStatus(0);
                        }
                    }else {
                        subjectOutNotifyLog.setMsg("没有交易状态");
                    }

                    int transStatus = notifyRequest.getTransStatus().intValue();
                    if (transStatus == 1 || transStatus == 2) {
                        //更新订单表状态
                        subjectOutBills.setStatus(transStatus);
                        subjectOutBillsService.updateById(subjectOutBills);
                        try {
                            //TODO 回调支付中台
                            subjectPayNotifyUtil.notifyPay(subjectOutBills.getSubjectCode(),"notify/df",notifyRequest);
                        }catch (Exception e){
                            log.error("回调支付中台异常，notifyRequest：{},error：{}",notifyRequest,e);
                        }
                    }
                }else {
                    subjectOutNotifyLog.setMsg("订单状态已确定,不处理");
                }
                result ="success";
            }else {
                //验签不通过
                //直接返回不处理业务
                subjectOutNotifyLog.setStatus(2);
                subjectOutNotifyLog.setMsg("快捷通支付回调_验签不通过");
                log.error("快捷通支付回调_验签失败,request:{}",request);
            }
        }

        //记录通知第三方支付公司的信息
        subjectOutNotifyLog.setResponseText(result);
        try {
            subjectOutNotifyLogService.updateById(subjectOutNotifyLog);
        }catch (Exception e){
            log.error("更新支付回调记录表失败,request:{},error:{}",request,e);
        }
        return result;
    }

    @Override
    public TransResult bindcardApply(BindCardDto bindCardDto) {
        TransResult bindCardResult = new TransResult();
        bindCardResult.setBillNo(bindCardDto.getBillNo());
        bindCardResult.setMerchantBillNo(bindCardDto.getMerchantBillNo());
        bindCardResult.setTransStatus(0);
        bindCardResult.setMsg("绑卡申请处理中");

        String category=bindCardDto.getChannelCode().getCode();

        String merchantNo=subjectChannelConfigService.getConfigString(category, ConfigKeyEnums.MERCHANTNO.getName());
        String requestUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.REQUESTURL.getName());
        String privateKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PRIVATEKEY.getName());
        String publicKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PUBLICKEY.getName());
        String callBackUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.CALLBACKURL.getName());
        String MD5Key=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MD5KEY.getName());

        if(StringUtils.isBlank(requestUrl) || StringUtils.isBlank(merchantNo) || StringUtils.isBlank(privateKey) || StringUtils.isBlank(publicKey) || StringUtils.isBlank(callBackUrl) || StringUtils.isBlank(MD5Key)){
            bindCardResult.setTransStatus(2);
            bindCardResult.setMsg(ErrorMsg.INCOMPLETE_CONFIG_INFO);
            return bindCardResult;
        }

        Map<String,Object> params = new HashMap<String, Object>();
        //银行卡账户名，不能包含数字
        params.put("bank_account_name",bindCardDto.getName());
        //证件类型 身份证
        params.put("certificates_type","01");
        //证件号码，与证件类型匹配使用
        params.put("certificates_number",bindCardDto.getIdCard());
        params.put("bank_card_no",bindCardDto.getCardNo());
        //支付产品码51-快捷借记卡，52-快捷贷记卡
        params.put("pay_product_code","51");
        params.put("phone_num",bindCardDto.getPhone());
        MultiValueMap<String, String> postParameters = generateRequestParams(params,KjtConfig.bindcard_service_apply,merchantNo,privateKey);

        ThirdProtocolBindLog thirdProtocolBindLog=new ThirdProtocolBindLog();
        thirdProtocolBindLog.setSubjectCode(bindCardDto.getSubjectCode());
        thirdProtocolBindLog.setMerchantBillNo(bindCardDto.getMerchantBillNo());
        thirdProtocolBindLog.setBillNo(bindCardDto.getBillNo());
        thirdProtocolBindLog.setStatus(0);
        thirdProtocolBindLog.setRequestUrl(requestUrl);
        thirdProtocolBindLog.setRequestText(JSON.toJSONString(params));
        thirdProtocolBindLog.setBeginTime(System.currentTimeMillis());
        thirdProtocolBindLog.setServiceType(bindCardDto.getServiceType());

        String str = null;
        try {
            str = restTemplate.postForObject(requestUrl,postParameters,String.class);
            str = AESCoder.decrypt(str, publicKey);
        } catch (Exception e) {
            log.error("调用快捷通协议绑卡申请接口异常,billNo:{},error:{}",bindCardDto.getBillNo(),e);
            bindCardResult.setMsg(ErrorMsg.PAY_INTERFACE_CALL_FAIL);
        }

        thirdProtocolBindLog.setResponseText((StringUtils.isNotBlank(str) && str.length()>4000)?str.substring(0,4000):str);
        thirdProtocolBindLog.setEndTime(System.currentTimeMillis());

        if (StringUtils.isBlank(str)) {
            //默认绑卡处理中
            log.error("调用快捷通协议支付绑卡申请接口返回空,billNo:{}",bindCardDto.getBillNo());
            bindCardResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
        }else {
            //TODO biz_content中字段的顺序不能变
            Gson gson = new Gson();
            Map<String, String> map = new HashMap<String, String>();
            map = gson.fromJson(str, map.getClass());
            if (null != map) {
                String sign = map.get("sign");
                String charset = map.get("charset");
                String bizContent = map.get("biz_content") == null ? null : JSON.toJSONString(map.get("biz_content"));
                map.remove("biz_content");
                map.put("biz_content", bizContent);
                boolean flag = false;
                try {
                    flag = this.verify(map, sign, charset, publicKey);
                } catch (Exception e) {
                    bindCardResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                    log.error("解析快捷通返回数据出错,str:{},erroe:{}", str, e);
                }
                if (flag) {
                    //验签成功
                    //网关返回码
                    String code = map.get("code");
                    String msg = map.get("msg");
                    String sub_msg = map.get("sub_msg");
                    String biz_content = map.get("biz_content");
                    if(StringUtils.isNotEmpty(sub_msg)) {
                        bindCardResult.setMsg(sub_msg);
                    }else{
                        bindCardResult.setMsg(msg);
                    }

                    if (StringUtils.equals("S10000", code)) {
                        //接口调用成功
                        JSONObject jsonObject = JSONObject.parseObject(biz_content);
                        //快捷通订单号
                        String token_id = jsonObject.getString("token_id");

                        //绑卡请求成功
                        bindCardResult.setTransStatus(1);
                        thirdProtocolBindLog.setStatus(1);
                        //TODO 快捷通绑卡有返回
                        bindCardResult.setValidCode(token_id);
                    }else {
                        //绑卡请求失败
                        bindCardResult.setTransStatus(2);
                        thirdProtocolBindLog.setStatus(2);
                    }
                } else {
                    bindCardResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                }
            } else {
                bindCardResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
            }
        }

        try {
            thirdProtocolBindLogService.insert(thirdProtocolBindLog);
        } catch (Exception e) {
            log.error("插入协议绑卡日志表异常,billNo:{},error:{}",bindCardDto.getBillNo(),e);
        }

        return bindCardResult;
    }

    @Override
    public TransResult bindcardConfirm(BindCardDto bindCardDto) {
        TransResult bindCardResult = new TransResult();
        bindCardResult.setBillNo(bindCardDto.getBillNo());
        bindCardResult.setMerchantBillNo(bindCardDto.getMerchantBillNo());
        bindCardResult.setTransStatus(0);
        bindCardResult.setMsg("绑卡确认处理中");

        String category=bindCardDto.getChannelCode().getCode();

        String merchantNo=subjectChannelConfigService.getConfigString(category, ConfigKeyEnums.MERCHANTNO.getName());
        String requestUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.REQUESTURL.getName());
        String privateKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PRIVATEKEY.getName());
        String publicKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PUBLICKEY.getName());
        String callBackUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.CALLBACKURL.getName());
        String MD5Key=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MD5KEY.getName());

        if(StringUtils.isBlank(requestUrl) || StringUtils.isBlank(merchantNo) || StringUtils.isBlank(privateKey) || StringUtils.isBlank(publicKey) || StringUtils.isBlank(callBackUrl) || StringUtils.isBlank(MD5Key)){
            bindCardResult.setTransStatus(2);
            bindCardResult.setMsg(ErrorMsg.INCOMPLETE_CONFIG_INFO);
            return bindCardResult;
        }

        Map<String,Object> params = new HashMap<String, Object>();
        //银行卡协议号
        params.put("token_id",bindCardDto.getValidCode());
        //手机验证码
        params.put("verify_code",bindCardDto.getSmsCode());
        MultiValueMap<String, String> postParameters = generateRequestParams(params,KjtConfig.bindcard_service_advance,merchantNo,privateKey);

        ThirdProtocolBindLog thirdProtocolBindLog=new ThirdProtocolBindLog();
        thirdProtocolBindLog.setSubjectCode(bindCardDto.getSubjectCode());
        thirdProtocolBindLog.setMerchantBillNo(bindCardDto.getMerchantBillNo());
        thirdProtocolBindLog.setBillNo(bindCardDto.getBillNo());
        thirdProtocolBindLog.setStatus(0);
        thirdProtocolBindLog.setRequestUrl(requestUrl);
        thirdProtocolBindLog.setRequestText(JSON.toJSONString(params));
        thirdProtocolBindLog.setBeginTime(System.currentTimeMillis());
        thirdProtocolBindLog.setServiceType(bindCardDto.getServiceType());

        String str = null;
        try {
            str = restTemplate.postForObject(requestUrl,postParameters,String.class);
            str = AESCoder.decrypt(str, publicKey);
        } catch (Exception e) {
            log.error("调用快捷通协议绑卡确认接口异常,billNo:{},error:{}",bindCardDto.getBillNo(),e);
            bindCardResult.setMsg(ErrorMsg.PAY_INTERFACE_CALL_FAIL);
        }

        thirdProtocolBindLog.setResponseText((StringUtils.isNotBlank(str) && str.length()>4000)?str.substring(0,4000):str);
        thirdProtocolBindLog.setEndTime(System.currentTimeMillis());

        if (StringUtils.isBlank(str)) {
            //默认绑卡处理中
            log.error("调用快捷通协议支付绑卡接口返回空,billNo:{}",bindCardDto.getBillNo());
            bindCardResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
        }else {
            //TODO biz_content中字段的顺序不能变
            Gson gson = new Gson();
            Map<String, String> map = new HashMap<String, String>();
            map = gson.fromJson(str, map.getClass());
            if (null != map) {
                String sign = map.get("sign");
                String charset = map.get("charset");
                String bizContent = map.get("biz_content") == null ? null : JSON.toJSONString(map.get("biz_content"));
                map.remove("biz_content");
                map.put("biz_content", bizContent);
                boolean flag = false;
                try {
                    flag = this.verify(map, sign, charset, publicKey);
                } catch (Exception e) {
                    bindCardResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                    log.error("解析快捷通返回数据出错,str:{},erroe:{}", str, e);
                }
                if (flag) {
                    //验签成功
                    //网关返回码
                    String code = map.get("code");
                    String msg = map.get("msg");
                    String sub_msg = map.get("sub_msg");
                    String biz_content = map.get("biz_content");
                    if(StringUtils.isNotEmpty(sub_msg)) {
                        bindCardResult.setMsg(sub_msg);
                    }else{
                        bindCardResult.setMsg(msg);
                    }

                    if (StringUtils.equals("S10000", code)) {
                        //绑卡确认成功
                        bindCardResult.setTransStatus(1);
                        thirdProtocolBindLog.setStatus(1);
                    }else {
                        //绑卡确认失败
                        bindCardResult.setTransStatus(2);
                        thirdProtocolBindLog.setStatus(2);
                        //短信校验失败
                        if(sub_msg.contains("验证码")) {
                            bindCardResult.setMsg(ErrorMsg.SMSCODE_FAIL);
                        }
                    }
                } else {
                    bindCardResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                }
            } else {
                bindCardResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
            }
        }

        try {
            thirdProtocolBindLogService.insert(thirdProtocolBindLog);
        } catch (Exception e) {
            log.error("插入协议绑卡日志表异常,billNo:{},error:{}",bindCardDto.getBillNo(),e);
        }

        return bindCardResult;
    }

    @Override
    public TransResult reSendSms(BindCardDto bindCardDto) {
        //快捷通重发短信验证码就是重新调了一遍绑卡申请
        TransResult bindCardResult = new TransResult();
        bindCardResult.setBillNo(bindCardDto.getBillNo());
        bindCardResult.setMerchantBillNo(bindCardDto.getMerchantBillNo());
        bindCardResult.setTransStatus(0);
        bindCardResult.setMsg("绑卡重发短信验证码处理中");

        String category=bindCardDto.getChannelCode().getCode();

        String merchantNo=subjectChannelConfigService.getConfigString(category, ConfigKeyEnums.MERCHANTNO.getName());
        String requestUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.REQUESTURL.getName());
        String privateKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PRIVATEKEY.getName());
        String publicKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PUBLICKEY.getName());
        String callBackUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.CALLBACKURL.getName());
        String MD5Key=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MD5KEY.getName());

        if(StringUtils.isBlank(requestUrl) || StringUtils.isBlank(merchantNo) || StringUtils.isBlank(privateKey) || StringUtils.isBlank(publicKey) || StringUtils.isBlank(callBackUrl) || StringUtils.isBlank(MD5Key)){
            bindCardResult.setTransStatus(2);
            bindCardResult.setMsg(ErrorMsg.INCOMPLETE_CONFIG_INFO);
            return bindCardResult;
        }

        Map<String,Object> params = new HashMap<String, Object>();
        //银行卡账户名，不能包含数字
        params.put("bank_account_name",bindCardDto.getName());
        //证件类型 身份证
        params.put("certificates_type","01");
        //证件号码，与证件类型匹配使用
        params.put("certificates_number",bindCardDto.getIdCard());
        params.put("bank_card_no",bindCardDto.getCardNo());
        //支付产品码51-快捷借记卡，52-快捷贷记卡
        params.put("pay_product_code","51");
        params.put("phone_num",bindCardDto.getPhone());
        MultiValueMap<String, String> postParameters = generateRequestParams(params,KjtConfig.bindcard_service_apply,merchantNo,privateKey);

        ThirdProtocolBindLog thirdProtocolBindLog=new ThirdProtocolBindLog();
        thirdProtocolBindLog.setSubjectCode(bindCardDto.getSubjectCode());
        thirdProtocolBindLog.setMerchantBillNo(bindCardDto.getMerchantBillNo());
        thirdProtocolBindLog.setBillNo(bindCardDto.getBillNo());
        thirdProtocolBindLog.setStatus(0);
        thirdProtocolBindLog.setRequestUrl(requestUrl);
        thirdProtocolBindLog.setRequestText(JSON.toJSONString(params));
        thirdProtocolBindLog.setBeginTime(System.currentTimeMillis());
        thirdProtocolBindLog.setServiceType(bindCardDto.getServiceType());

        String str = null;
        try {
            str = restTemplate.postForObject(requestUrl,postParameters,String.class);
            str = AESCoder.decrypt(str, publicKey);
        } catch (Exception e) {
            log.error("调用快捷通协议绑卡重发短信验证码接口异常,billNo:{},error:{}",bindCardDto.getBillNo(),e);
            bindCardResult.setMsg(ErrorMsg.PAY_INTERFACE_CALL_FAIL);
        }

        thirdProtocolBindLog.setResponseText((StringUtils.isNotBlank(str) && str.length()>4000)?str.substring(0,4000):str);
        thirdProtocolBindLog.setEndTime(System.currentTimeMillis());

        if (StringUtils.isBlank(str)) {
            //默认绑卡处理中
            log.error("调用快捷通协议支付重发短信验证码接口返回空,billNo:{}",bindCardDto.getBillNo());
            bindCardResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
        }else {
            //TODO biz_content中字段的顺序不能变
            Gson gson = new Gson();
            Map<String, String> map = new HashMap<String, String>();
            map = gson.fromJson(str, map.getClass());
            if (null != map) {
                String sign = map.get("sign");
                String charset = map.get("charset");
                String bizContent = map.get("biz_content") == null ? null : JSON.toJSONString(map.get("biz_content"));
                map.remove("biz_content");
                map.put("biz_content", bizContent);
                boolean flag = false;
                try {
                    flag = this.verify(map, sign, charset, publicKey);
                } catch (Exception e) {
                    bindCardResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                    log.error("解析快捷通返回数据出错,str:{},erroe:{}", str, e);
                }
                if (flag) {
                    //验签成功
                    //网关返回码
                    String code = map.get("code");
                    String msg = map.get("msg");
                    String sub_msg = map.get("sub_msg");
                    String biz_content = map.get("biz_content");
                    if(StringUtils.isNotEmpty(sub_msg)) {
                        bindCardResult.setMsg(sub_msg);
                    }else{
                        bindCardResult.setMsg(msg);
                    }

                    if (StringUtils.equals("S10000", code)) {
                        //接口调用成功
                        JSONObject jsonObject = JSONObject.parseObject(biz_content);
                        //快捷通订单号
                        String token_id = jsonObject.getString("token_id");

                        //绑卡请求成功
                        bindCardResult.setTransStatus(1);
                        thirdProtocolBindLog.setStatus(1);
                        //TODO 快捷通绑卡有返回
                        bindCardResult.setValidCode(token_id);
                    }else {
                        //绑卡请求失败
                        bindCardResult.setTransStatus(2);
                        thirdProtocolBindLog.setStatus(2);
                    }
                } else {
                    bindCardResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                }
            } else {
                bindCardResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
            }
        }

        try {
            thirdProtocolBindLogService.insert(thirdProtocolBindLog);
        } catch (Exception e) {
            log.error("插入协议绑卡日志表异常,billNo:{},error:{}",bindCardDto.getBillNo(),e);
        }

        return bindCardResult;
    }

    @Override
    public TransResult protocolPay(TransRequest request) {
        TransResult transResult = new TransResult();
        transResult.setBillNo(request.getBillNo());
        transResult.setMerchantBillNo(request.getMerchantBillNo());
        transResult.setAmount(request.getAmount());
        transResult.setTransStatus(0);
        transResult.setTransTime(System.currentTimeMillis());

        String category=request.getChannelCode().getCode();

        String merchantNo=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MERCHANTNO.getName());
        String requestUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.REQUESTURL.getName());
        String privateKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PRIVATEKEY.getName());
        String publicKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PUBLICKEY.getName());
        String callBackUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.CALLBACKURL.getName());
        String MD5Key=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MD5KEY.getName());

        if(StringUtils.isBlank(requestUrl) || StringUtils.isBlank(merchantNo) || StringUtils.isBlank(privateKey) || StringUtils.isBlank(publicKey) || StringUtils.isBlank(callBackUrl) || StringUtils.isBlank(MD5Key)){
            transResult.setTransStatus(2);
            transResult.setMsg(ErrorMsg.INCOMPLETE_CONFIG_INFO);
            return transResult;
        }

        Map<String,Object> params = new HashMap<String, Object>();
        //买家公网IP地址************
        params.put("payer_ip","101.81.117.131");
        Map<String,String> tradeInfoMap=new HashMap<String, String>();
        tradeInfoMap.put("terminal_type","99");
        tradeInfoMap.put("ip","101.81.117.131");
        //交易信息
        TradeInfo tradeInfo=new TradeInfo();
        //平台(商户)订单号
        tradeInfo.setOut_trade_no(request.getBillNo());
        //币种，默认人民币CNY
        tradeInfo.setCurrency("CNY");
        //交易金额，交易金额=(商品单价×商品数量)
        tradeInfo.setTotal_amount(request.getAmount().setScale(2).toString());
        //卖家标识类型，1-卖家会员ID ,2-卖家登录账号
        tradeInfo.setPayee_identity_type("1");
        //卖家会员ID或登录账号
        tradeInfo.setPayee_identity(merchantNo);
        //服务器异步通知地址
        tradeInfo.setNotify_url(callBackUrl);
        //商品数量
        tradeInfo.setQuantity("1");
        //商品名称
        tradeInfo.setSubject("商品");
        //单价
        tradeInfo.setPrice(request.getAmount().setScale(2).toString());
        //支付方式
        PayMethod payMethod=new PayMethod();
        //支付产品码51-快捷借记卡，52-快捷贷记卡
        payMethod.setPay_product_code("51");
        //应付金额
        payMethod.setAmount(request.getAmount().setScale(2).toString());
        //协议号
        payMethod.setToken_id(request.getProtocolNo());
        //终端信息
        params.put("terminal_info",tradeInfoMap);
        //交易信息
        params.put("trade_info",tradeInfo);
        //出款快捷通会员标识类型，默认1  1-快捷通会员ID  2-快捷通会员登录号
        params.put("payer_identity_type","1");
        //买家账号
        params.put("payer_identity","anonymous");
        //收银台类型
        params.put("cashier_type","API");
        //支付方式
        params.put("pay_method",payMethod);
        //收银台类型
        params.put("biz_product_code","20601");
        //订单允许的最晚付款时间
        params.put("timeout_express","10m");
        MultiValueMap<String, String> postParameters = generateRequestParams(params,KjtConfig.xyreapy_service,merchantNo,privateKey);

        SubjectInThirdBills subjectInThirdBills=new SubjectInThirdBills();
        subjectInThirdBills.setSubjectCode(request.getSubjectCode());
        subjectInThirdBills.setMerchantBillNo(request.getMerchantBillNo());
        subjectInThirdBills.setBillNo(request.getBillNo());
        subjectInThirdBills.setStatus(0);
        subjectInThirdBills.setAmount(request.getAmount());
        subjectInThirdBills.setRequestUrl(requestUrl);
        subjectInThirdBills.setRequestText(JSON.toJSONString(postParameters));
        subjectInThirdBills.setBeginTime(System.currentTimeMillis());
        subjectInThirdBills.setServiceType(request.getServiceType());

        String str = null;
        try {
            str = restTemplate.postForObject(requestUrl,postParameters,String.class);
            str = AESCoder.decrypt(str, publicKey);
        } catch (Exception e) {
            log.error("调用快捷通协议支付接口异常,billNo:{},error:{}",request.getBillNo(),e);
            transResult.setMsg(ErrorMsg.PAY_INTERFACE_CALL_FAIL);
        }

        subjectInThirdBills.setResponseText((StringUtils.isNotBlank(str) && str.length()>4000)?str.substring(0,4000):str);
        subjectInThirdBills.setEndTime(System.currentTimeMillis());

        if (StringUtils.isBlank(str)) {
            //默认交易处理中
            log.error("调用快捷通协议支付接口返回空,billNo:{}",request.getBillNo());
            transResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
        }else {
            //TODO biz_content中字段的顺序不能变
            Gson gson = new Gson();
            Map<String, String> map = new HashMap<String, String>();
            map = gson.fromJson(str, map.getClass());
            if (null != map) {
                String sign = map.get("sign");
                String charset = map.get("charset");
                String bizContent = map.get("biz_content") == null ? null : JSON.toJSONString(map.get("biz_content"));
                map.remove("biz_content");
                map.put("biz_content", bizContent);
                boolean flag = false;
                try {
                    flag = this.verify(map, sign, charset, publicKey);
                } catch (Exception e) {
                    transResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                    log.error("代付解析快捷通返回数据出错,str:{},erroe:{}", str, e);
                }
                if (flag) {
                    //验签成功
                    //网关返回码
                    String code = map.get("code");
                    String msg = map.get("msg");
                    String biz_content = map.get("biz_content");
                    transResult.setMsg(msg);

                    if (StringUtils.equals("S10000", code)) {
                        //接口调用成功
                        JSONObject jsonObject = JSONObject.parseObject(biz_content);
                        //快捷通订单号
                        String trade_no = jsonObject.getString("trade_no");
                        //订单状态，S-成功 P-处理中 F-失败
                        String status = jsonObject.getString("status");
                        transResult.setTransNo(trade_no);
                        if (StringUtils.equals("S", status)) {
                            transResult.setTransTime(System.currentTimeMillis());
                            transResult.setTransStatus(1);
                            subjectInThirdBills.setStatus(1);
                        } else if (StringUtils.equals("F", status)) {
                            transResult.setTransStatus(2);
                            subjectInThirdBills.setStatus(2);
                        } else {//处理中
                            transResult.setTransStatus(0);
                            subjectInThirdBills.setStatus(0);
                        }
                    }
                } else {
                    transResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                }
            } else {
                transResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
            }
        }

        try {
            subjectInThirdBillsService.insert(subjectInThirdBills);
        } catch (Exception e) {
            log.error("插入第三方订单表异常,billNo:{},error:{}",request.getBillNo(),e);
        }

        return transResult;
    }

    @Override
    public TransResult protocolPayQuery(SubjectpayQueryRequest request) {
        TransResult transResult = new TransResult();
        transResult.setBillNo(request.getBillNo());
        transResult.setMerchantBillNo(request.getMerchantBillNo());
        transResult.setAmount(request.getAmount());
        transResult.setTransStatus(0);
        transResult.setTransTime(System.currentTimeMillis());

        String category=request.getChannelCode();

        String merchantNo=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MERCHANTNO.getName());
        String requestUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.REQUESTURL.getName());
        String privateKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PRIVATEKEY.getName());
        String publicKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PUBLICKEY.getName());
        String callBackUrl=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.CALLBACKURL.getName());
        String MD5Key=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.MD5KEY.getName());

        if(StringUtils.isBlank(requestUrl) || StringUtils.isBlank(merchantNo) || StringUtils.isBlank(privateKey) || StringUtils.isBlank(publicKey) || StringUtils.isBlank(callBackUrl) || StringUtils.isBlank(MD5Key)){
            transResult.setTransStatus(2);
            transResult.setMsg(ErrorMsg.INCOMPLETE_CONFIG_INFO);
            return transResult;
        }

        Map<String,Object> params = new HashMap<String, Object>();
        //平台(商户)订单号
        params.put("out_trade_no",request.getBillNo());
        MultiValueMap<String, String> postParameters = this.generateRequestParams(params,KjtConfig.reapy_query,merchantNo,privateKey);

        SubjectInThirdBills subjectInThirdBills=new SubjectInThirdBills();
        subjectInThirdBills.setSubjectCode(request.getSubjectCode());
        subjectInThirdBills.setMerchantBillNo(request.getMerchantBillNo());
        subjectInThirdBills.setBillNo(request.getBillNo());
        subjectInThirdBills.setStatus(0);
        subjectInThirdBills.setAmount(request.getAmount());
        subjectInThirdBills.setRequestUrl(requestUrl);
        subjectInThirdBills.setRequestText(JSON.toJSONString(postParameters));
        subjectInThirdBills.setBeginTime(System.currentTimeMillis());
        subjectInThirdBills.setServiceType(request.getServiceType());

        String str = null;
        try {
            str = restTemplate.postForObject(requestUrl,postParameters,String.class);
            str = AESCoder.decrypt(str, publicKey);
        } catch (Exception e) {
            log.error("调用快捷通协议支付查询接口异常,billNo:{},error:{}",request.getBillNo(),e);
            transResult.setMsg(ErrorMsg.PAY_INTERFACE_CALL_FAIL);
        }

        subjectInThirdBills.setResponseText((StringUtils.isNotBlank(str) && str.length()>4000)?str.substring(0,4000):str);
        subjectInThirdBills.setEndTime(System.currentTimeMillis());

        if (StringUtils.isBlank(str)) {
            //默认交易处理中
            log.error("调用快捷通协议支付查询接口返回空,billNo:{}",request.getBillNo());
            transResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
        }else {
            //TODO biz_content中字段的顺序不能变
            Gson gson = new Gson();
            Map<String, String> map = new HashMap<String, String>();
            map = gson.fromJson(str, map.getClass());
            if (null != map) {
                String sign = map.get("sign");
                String charset = map.get("charset");
                String bizContent = map.get("biz_content") == null ? null : JSON.toJSONString(map.get("biz_content"));
                map.remove("biz_content");
                map.put("biz_content", bizContent);
                boolean flag = false;
                try {
                    flag = this.verify(map, sign, charset, publicKey);
                } catch (Exception e) {
                    transResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                    log.error("代付解析快捷通返回数据出错,str:{},erroe:{}", str, e);
                }
                if (flag) {
                    //验签成功
                    //网关返回码
                    String code = map.get("code");
                    String biz_content = map.get("biz_content");
                    String msg = map.get("sub_msg");
                    transResult.setMsg(msg);

                    if (StringUtils.equals("S10000", code)) {
                        //接口调用成功
                        JSONObject jsonObject = JSONObject.parseObject(biz_content);
                        //快捷通订单号
                        String trade_no = jsonObject.getString("trade_no");
                        //交易状态对应的时间，格式"yyyy-MM-dd HH:mm:ss"
                        String transTime=jsonObject.getString("modify_time");
                        //转账状态 submitted提交银行成功 failed出款失败 success出款成功 如为failed 时错误编码和错误原因放入sub_code和sub_msg中
                        String status = jsonObject.getString("status");
                        transResult.setTransNo(trade_no);
                        if (StringUtils.isNotBlank(transTime) && !StringUtils.equals("NULL",transTime) && !StringUtils.equals("null",transTime)){
                            transResult.setTransTime(DateUtils.convert(transTime,DateUtils.DATE_TIME_FORMAT).getTime());
                        }
                        if (StringUtils.equals("TRADE_SUCCESS",status) || StringUtils.equals("TRADE_FINISHED",status) ){
                            transResult.setTransStatus(1);
                            subjectInThirdBills.setStatus(1);
                        }else if (StringUtils.equals("TRADE_CLOSED",status)){
                            transResult.setTransStatus(2);
                            subjectInThirdBills.setStatus(2);
                        } else {//处理中
                            transResult.setTransStatus(0);
                            subjectInThirdBills.setStatus(0);
                        }
                    }
                } else {
                    transResult.setMsg(ErrorMsg.RETURN_DATA_CHECK_SIGN_FAIL);
                }
            } else {
                transResult.setMsg(ErrorMsg.PAY_INTERFACE_RETURN_BLANK);
            }
        }

        try {
            subjectInThirdBillsService.insert(subjectInThirdBills);
        } catch (Exception e) {
            log.error("快捷通协议支付插入第三方订单表异常,billNo:{},error:{}",request.getBillNo(),e);
        }

        return transResult;
    }

    @Override
    public String protocolPayNotify(KjtpayRepayCallBackRequest request) {
        String result ="";
        //记录回调原始数据
        SubjectInNotifyLog subjectInNotifyLog = new SubjectInNotifyLog();
        subjectInNotifyLog.setBillNo(request.getOuter_trade_no());
        subjectInNotifyLog.setCallbackText(JSONObject.toJSONString(request));
        //默认回调处理状态为处理中
        subjectInNotifyLog.setStatus(0);
        //默认订单交易状态为处理中
        subjectInNotifyLog.setTransStatus(0);
        subjectInNotifyLog.setCallbackText(JSONObject.toJSONString(request));
        subjectInNotifyLog.setMsg("回调处理中");
        boolean rt = subjectInNotifyLogService.insert(subjectInNotifyLog);
        if (!rt){
            log.error("插入回调日志表失败,request:{}",request);
            return result;
        }

        //查询回调对应的订单信息
        SubjectInBills subjectInBillsFind = new SubjectInBills();
        subjectInBillsFind.setBillNo(request.getOuter_trade_no());
        SubjectInBills subjectInBills = subjectInBillsService.getLastOneInBills(subjectInBillsFind);
        if(null==subjectInBills){
            log.error("未查询到该笔订单,request:{}",request);
            return result;
        }
        subjectInNotifyLog.setSubjectCode(subjectInBills.getSubjectCode());
        subjectInNotifyLog.setMerchantBillNo(subjectInBills.getMerchantBillNo());

        //转发到支付中台的回调对象
        NotifyRequest notifyRequest=new NotifyRequest();
        notifyRequest.setMerchantBillNo(subjectInBills.getMerchantBillNo());
        notifyRequest.setBillNo(subjectInBills.getBillNo());
        notifyRequest.setTransStatus(0);
        notifyRequest.setMsg("回调处理中");
        notifyRequest.setTransTime(System.currentTimeMillis());
        notifyRequest.setAmount(subjectInBills.getAmount());

        String category=subjectInBills.getChannelCode();
        String publicKey=subjectChannelConfigService.getConfigString(category,ConfigKeyEnums.PUBLICKEY.getName());

        Map<String,String> map = JSONObject.parseObject(JSONObject.toJSONString(request),Map.class);
        if (null != map) {
            String sign = map.get("sign");
            map.put("_input_charset", "UTF-8");
            boolean flag = false;
            try {
                flag =this.verify(map, sign, "UTF-8", publicKey);
            } catch (Exception e) {
                log.error("代付解析快捷通返回数据出错,notify_id:{},error:{}",request.getNotify_id(),e);
            }

            if(flag){
                String transNo=request.getInner_trade_no();
                //WITHDRAWAL_SUCCESS:成功  WITHDRAWAL_FAIL：失败 RETURN_TICKET：退票
                String status=request.getTrade_status();
                //支付时间 要转换 yyyyMMddHHmmss
                String tradeTime=request.getGmt_payment();
                //失败原因
                String fail_reason=request.getFailReason();

                subjectInNotifyLog.setStatus(1);
                subjectInNotifyLog.setTransNo(transNo);
                subjectInNotifyLog.setMsg(fail_reason);
                notifyRequest.setTransNo(transNo);
                notifyRequest.setMsg(fail_reason);

                if ((subjectInBills.getStatus().intValue() == 0 || subjectInBills.getStatus().intValue() == 3)){
                    if(StringUtils.isNotBlank(status)){
                        if(StringUtils.equals("TRADE_SUCCESS",status) || StringUtils.equals("TRADE_FINISHED",status)){
                            //支付成功
                            if(StringUtils.isNotBlank(tradeTime)){
                                Long time=DateUtils.convert(tradeTime,DateUtils.DATE_TIMESTAMP_SHORT_FORMAT).getTime();
                                notifyRequest.setTransTime(time);
                            }else{
                                notifyRequest.setTransTime(System.currentTimeMillis());
                            }

                            notifyRequest.setTransStatus(1);
                            subjectInNotifyLog.setTransStatus(1);
                        }else if(StringUtils.equals("TRADE_CLOSED",status)){
                            //支付失败
                            notifyRequest.setTransStatus(2);
                            subjectInNotifyLog.setTransStatus(2);
                        } else {
                            //支付处理中
                            notifyRequest.setTransStatus(0);
                            subjectInNotifyLog.setTransStatus(0);
                        }
                    }else {
                        subjectInNotifyLog.setMsg("没有交易状态");
                    }

                    int transStatus = notifyRequest.getTransStatus().intValue();
                    if (transStatus == 1 || transStatus == 2) {
                        //更新订单表状态
                        subjectInBills.setStatus(transStatus);
                        subjectInBillsService.updateById(subjectInBills);
                        try {
                            //TODO 回调支付中台
                            subjectPayNotifyUtil.notifyPay(subjectInBills.getSubjectCode(),"notify/protocolPay",notifyRequest);
                        }catch (Exception e){
                            log.error("回调支付中台异常，notifyRequest：{},error：{}",notifyRequest,e);
                        }
                    }
                }else {
                    subjectInNotifyLog.setMsg("订单状态已确定,不处理");
                }
                result ="success";
            }else {
                //验签不通过
                //直接返回不处理业务
                subjectInNotifyLog.setStatus(2);
                subjectInNotifyLog.setMsg("快捷通支付回调_验签不通过");
                log.error("快捷通支付回调_验签失败,request:{}",request);
            }
        }

        //记录通知第三方支付公司的信息
        subjectInNotifyLog.setResponseText(result);
        try {
            subjectInNotifyLogService.updateById(subjectInNotifyLog);
        }catch (Exception e){
            log.error("更新支付回调记录表失败,request:{},error:{}",request,e);
        }
        return result;
    }


    /**
     * 组装请求业务参数
     * @param bizParams 业务参数
     * @param serviceName 服务名称
     * @param partnerId 商户id
     * @param privateKey 私钥
     * @return
     */
    private MultiValueMap<String, String> generateRequestParams(Map<String,Object> bizParams,String serviceName,String partnerId,String privateKey){
        String charset = "UTF-8";
        String sign_type = "RSA";
        //业务接口请求报文支持的格式，仅支持 JSON
        String format = "JSON";
        Map<String,String> params=new HashMap<>();
        String request_no = UUID.randomUUID().toString().replaceAll("-","");
        //请求号，字母数字下划线，确保每次请求唯一
        params.put("request_no",request_no);
        //接口名称
        params.put("service",serviceName);
        params.put("version","1.0");
        //签约后分配的快捷通平台(商户)ID，非平台则使用商户ID
        params.put("partner_id",partnerId);
        params.put("charset",charset);
        //请求时间，格式"yyyy-MM-dd HH:mm:ss"，如2017-06-24 13:07:56，请求时间与当前时间不能超过10分钟。若超过10分钟该请求会作为失败处理。
        params.put("timestamp",DateUtils.convert(new Date(),DateUtils.DATE_TIME_FORMAT));
        params.put("format",format);
        //业务接口请求报文（json 格式），最大长度不限，除公共请求报文外所有业务接口的请求报文都必须放在这个字段中传递，具体参照各业务接口报文
        params.put("biz_content",this.encrypt(JSONObject.toJSONString(bizParams),charset,privateKey));
        String str=this.createLinkString(params);
        String signStr= null;
        try {
            signStr = this.sign(str,privateKey,charset);
        } catch (Exception e) {
            log.error("快捷通加签失败");
        }
        params.put("sign",signStr);
        params.put("sign_type",sign_type);

        MultiValueMap<String, String> postParameters = new LinkedMultiValueMap<String, String>();
        for (Map.Entry<String, String> entry : params.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            try {
                if (StringUtils.isNotBlank(value)){
                    postParameters.add(key, URLEncoder.encode(value,"UTF-8"));
                }
            } catch (UnsupportedEncodingException e) {
                log.error("URLEncode失败",e);
            }
        }
        return postParameters;
    }


    /**
     * 加签
     * @param text
     * @param privateKey
     * @param charset
     * @return
     */
    public String sign(String text, String privateKey, String charset) {
        byte[] keyBytes = org.apache.commons.codec.binary.Base64.decodeBase64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);

        try {
            KeyFactory e = KeyFactory.getInstance("RSA");
            PrivateKey privateK = e.generatePrivate(pkcs8KeySpec);
            Signature signature = Signature.getInstance("SHA1withRSA");
            signature.initSign(privateK);
            signature.update(this.getContentBytes(text, charset));
            byte[] result = signature.sign();
            return org.apache.commons.codec.binary.Base64.encodeBase64String(result);
        } catch (Exception var9) {
            log.error("签名异常", var9);
            return null;
        }
    }

    /**
     * 验签
     * @param params
     * @param sign
     * @param charset
     * @param publicKey
     * @return
     */
    public boolean verify(Map<String, String> params, String sign, String charset,String publicKey) {
        String oriText = createLinkString(params);
        byte[] keyBytes = org.apache.commons.codec.binary.Base64.decodeBase64(publicKey);
        X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);

        boolean  result;
        try {
            KeyFactory ex = KeyFactory.getInstance("RSA");
            PublicKey publicK = ex.generatePublic(keySpec);
            Signature signature = Signature.getInstance("SHA1withRSA");
            signature.initVerify(publicK);
            signature.update(this.getContentBytes(oriText, charset));
            result = signature.verify(org.apache.commons.codec.binary.Base64.decodeBase64(sign));
        } catch (Exception var11) {
            result=false;
        }

        return result;
    }

    /**
     * 加密
     * @param oriText
     * @param charset
     * @param privateKey
     * @return
     */
    public String encrypt(String oriText, String charset,String privateKey) {
        try {
            byte[] e = org.apache.commons.codec.binary.Base64.decodeBase64(privateKey);
            PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(e);
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateK = keyFactory.generatePrivate(pkcs8KeySpec);
            int keyLen = ((RSAPrivateCrtKeyImpl)privateK).getModulus().toString(2).length();
            Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
            cipher.init(1, privateK);
            byte[] oriBytes = this.getContentBytes(oriText, charset);
            int inputLen = oriBytes.length;
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int offSet = 0;
            int i = 0;

            for(int m = keyLen / 8 - 11; inputLen - offSet > 0; offSet = i * m) {
                byte[] cache;
                if(inputLen - offSet > m) {
                    cache = cipher.doFinal(oriBytes, offSet, m);
                } else {
                    cache = cipher.doFinal(oriBytes, offSet, inputLen - offSet);
                }

                out.write(cache, 0, cache.length);
                ++i;
            }

            byte[] encryptedData = out.toByteArray();
            out.close();
            return org.apache.commons.codec.binary.Base64.encodeBase64String(encryptedData);
        } catch (Exception var17) {
            log.error("加密异常", var17);
            return null;
        }
    }

    /**
     * 字符串转化成byte
     * @param content
     * @param charset
     * @return
     */
    private byte[] getContentBytes(String content, String charset) {
        if(StringUtils.isBlank(charset)) {
            return content.getBytes();
        } else {
            try {
                return content.getBytes(charset);
            } catch (UnsupportedEncodingException var4) {
                log.error("签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:{}", charset);
                throw new RuntimeException("签名过程中出现错误,指定的编码集不对,您目前指定的编码集是:" + charset);
            }
        }
    }

    /**
     * map转化成String 排序 &连接
     * @param params
     * @return
     */
    private String createLinkString(Map<String, String> params) {
        if(params != null && params.size() > 0) {
            params = paraFilter(params);
            ArrayList keys = new ArrayList(params.keySet());
            Collections.sort(keys);
            StringBuffer prestr = new StringBuffer();

            for(int i = 0; i < keys.size(); ++i) {
                String key = (String)keys.get(i);
                String value = (String)params.get(key);
                if(i == keys.size() - 1) {
                    prestr.append(key + "=" + value);
                } else {
                    prestr.append(key + "=" + value + "&");
                }
            }

            return prestr.toString();
        } else {
            return null;
        }
    }

    /**
     * 过滤参数 不为空，不包含sign和sign_type字段
     * @param oriMap
     * @return
     */
    private static Map<String,String> paraFilter(Map<String, String> oriMap) {
        HashMap result = new HashMap();
        if(oriMap != null && oriMap.size() > 0) {
            Iterator var2 = oriMap.entrySet().iterator();

            while(var2.hasNext()) {
                Map.Entry iterator = (Map.Entry)var2.next();
                String value = (String)iterator.getValue();
                if(!StringUtils.isBlank(value) && !((String)iterator.getKey()).equalsIgnoreCase("sign") && !((String)iterator.getKey()).equalsIgnoreCase("sign_type")) {
                    result.put(iterator.getKey(), value);
                }
            }
            return result;
        } else {
            return result;
        }
    }

}

